#![allow(dead_code)]

use ash::{
    ext::debug_utils, khr::{swapchain, surface}, prelude::VkResult, vk::{
        self, make_api_version, AccessFlags, BufferMemoryBarrier, CommandBufferLevel, CommandBufferUsageFlags, CommandPool, DependencyFlags, Extent3D, Handle, ImageAspectFlags, ImageLayout, Offset3D, PFN_vkCreateWin32SurfaceKHR, PipelineStageFlags, QueueFlags, Result, SharingMode, SurfaceKHR
    }, Device, Entry, Instance
};
use getset::{CloneGetters, CopyGetters, Getters, MutGetters, Setters, WithSetters};
use std::{
    any::type_name, default, ffi::{c_char, CStr, CString}, mem::*, os::raw::*, ptr::{null, null_mut}, sync::{Arc, LazyLock, Mutex}
};
use wgpu::rwh::{HasDisplayHandle, HasWindowHandle, RawWindowHandle};
use winit::{dpi::*, platform::windows::*, window::Window};

//
static VULKAN_APPLICATION_NAME: &str = "MY_VULKAN_APPLICATION_NAME";
static VULKAN_ENGINE_NAME: &str = "MY_VULKAN_ENGINE_NAME";
static ENABLE_VALIDATION_LAYERS: bool = true;
static REQUIRED_LAYERS: [&str; 1] = ["VK_LAYER_KHRONOS_validation"];
static CREATE_SURFACE_FUNC_NAME: &str = "vkCreateWin32SurfaceKHR";


// get proc address
pub fn get_instance_proc_addr(entry: &Entry, instance: vk::Instance, name: &str) -> vk::PFN_vkVoidFunction {
    let pfn_name = CString::new(name).unwrap();
    let pfn = unsafe {entry.get_instance_proc_addr(instance, pfn_name.as_ptr())};
    return pfn;
}

pub fn get_device_proc_addr(instance: &Instance, device: vk::Device, name: &str) -> vk::PFN_vkVoidFunction {
    let pfn_name = CString::new(name).unwrap();
    let pfn = unsafe {instance.get_device_proc_addr(device, pfn_name.as_ptr())};
    return pfn;
}

// get converted proc address
pub fn get_instance_proc_addr_converted<T>(entry: &Entry, instance: vk::Instance, name: &str) -> Option<T> {
    let pfn_name = CString::new(name).unwrap();
    let pfn = unsafe {entry.get_instance_proc_addr(instance, pfn_name.as_ptr())};
    
    if let Some(p) = pfn {
        let t_pfn: T = unsafe { transmute_copy(&p) };
        return Some(t_pfn);
    } else {
        return None;
    }
}

pub fn get_device_proc_addr_converted<T>(instance: &Instance, device: vk::Device, name: &str) -> Option<T> {
    // let ty_name = type_name::<T>();
    let pfn_name = CString::new(name).unwrap();
    let pfn = unsafe {instance.get_device_proc_addr(device, pfn_name.as_ptr())};
    if let Some(p) = pfn {
        let t_pfn: T = unsafe { transmute_copy(&p) };
        return Some(t_pfn);
    } else {
        return None;
    }
}

// vulkan surface create
pub fn create_vulkan_surface_win32(
    entry: &Entry,
    instance: &Instance,
    wnd: &Window,
) -> Option<SurfaceKHR> {
    let pfn_name = CString::new(CREATE_SURFACE_FUNC_NAME).unwrap();
    let pfn: PFN_vkCreateWin32SurfaceKHR = unsafe {
        transmute(
            entry
                .get_instance_proc_addr(instance.handle(), pfn_name.as_ptr())
                .unwrap(),
        )
    };
    let raw_handle = wnd.window_handle().unwrap().as_raw();
    if let RawWindowHandle::Win32(hdl) = raw_handle {
        let hwnd = hdl.hwnd.get();
        let hins = hdl.hinstance.unwrap().get();
        let surf_info = vk::Win32SurfaceCreateInfoKHR::default()
            .hwnd(hwnd)
            .hinstance(hins);

        let mut surf: SurfaceKHR = SurfaceKHR::default();
        let rst = unsafe { pfn(instance.handle(), &surf_info, null(), &mut surf) };
        if rst != Result::SUCCESS {
            println!("create_vulkan_surface_win32: failed");
            println!("result: {}", rst);
        } else {
            return Some(surf);
        }
    }
    return None;
}

// ex and layer helper
pub fn get_layer_names_and_pointers() -> (Vec<CString>, Vec<*const c_char>) {
    let layer_names = REQUIRED_LAYERS
        .iter()
        .map(|name| CString::new(*name).unwrap())
        .collect::<Vec<_>>();
    let layer_names_ptrs = layer_names
        .iter()
        .map(|name| name.as_ptr())
        .collect::<Vec<_>>();
    (layer_names, layer_names_ptrs)
}

pub fn check_validation_layer_support(entry: &Entry) {
    let supported_layers = unsafe { entry.enumerate_instance_layer_properties().unwrap() };
    for required in REQUIRED_LAYERS.iter() {
        let found = supported_layers.iter().any(|layer| {
            let name = unsafe { CStr::from_ptr(layer.layer_name.as_ptr()) };
            let name = name.to_str().expect("Failed to get layer name pointer");
            required == &name
        });
        if !found {
            panic!("Validation layer not supported: {}", required);
        }
    }
}

pub fn check_instance_extension_support(entry: &Entry, desired: &Vec<*const i8>) {
    let supported_exs = unsafe { entry.enumerate_instance_extension_properties(None).unwrap() };
    for ex in desired.iter() {
        let ex_name = unsafe { CStr::from_ptr(*ex) };
        let found: bool = supported_exs.iter().any(|supported_ex_prop| {
            let supported_name =
                unsafe { CStr::from_ptr(supported_ex_prop.extension_name.as_ptr()) };
            ex_name == supported_name
        });
        if !found {
            panic!("instance extension not supported: {:?}", ex_name);
        }
    }
}

// debug callback
unsafe extern "system" fn vulkan_debug_callback(
    flag: vk::DebugUtilsMessageSeverityFlagsEXT,
    typ: vk::DebugUtilsMessageTypeFlagsEXT,
    p_callback_data: *const vk::DebugUtilsMessengerCallbackDataEXT,
    _: *mut c_void,
) -> vk::Bool32 {
    use vk::DebugUtilsMessageSeverityFlagsEXT as Flag;
    let message = unsafe { CStr::from_ptr((*p_callback_data).p_message) };
    match flag {
        Flag::VERBOSE => log::debug!("{:?} - {:?}", typ, message),
        Flag::INFO => log::info!("{:?} - {:?}", typ, message),
        Flag::WARNING => log::warn!("{:?} - {:?}", typ, message),
        _ => log::error!("{:?} - {:?}", typ, message),
    }
    vk::FALSE
}

/// Setup the debug message if validation layers are enabled.
pub fn setup_debug_messenger(
    entry: &Entry,
    instance: &Instance,
) -> Option<(debug_utils::Instance, vk::DebugUtilsMessengerEXT)> {
    if !ENABLE_VALIDATION_LAYERS {
        return None;
    }

    let create_info = vk::DebugUtilsMessengerCreateInfoEXT::default()
        .flags(vk::DebugUtilsMessengerCreateFlagsEXT::empty())
        .message_severity(
            vk::DebugUtilsMessageSeverityFlagsEXT::ERROR
                | vk::DebugUtilsMessageSeverityFlagsEXT::WARNING
                | vk::DebugUtilsMessageSeverityFlagsEXT::INFO,
        )
        .message_type(
            vk::DebugUtilsMessageTypeFlagsEXT::GENERAL
                | vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION
                | vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE,
        )
        .pfn_user_callback(Some(vulkan_debug_callback));

    let debug_utils = debug_utils::Instance::new(entry, instance);
    let debug_utils_messenger = unsafe {
        debug_utils
            .create_debug_utils_messenger(&create_info, None)
            .unwrap()
    };
    Some((debug_utils, debug_utils_messenger))
}

// NjmDevice
#[derive(Getters, Setters, WithSetters, MutGetters, CopyGetters, CloneGetters)]
pub struct NjmDevice {
    #[getset(get = "pub")]
    entry: Entry,

    #[getset(get = "pub")]
    debug_tools: Option<(debug_utils::Instance, vk::DebugUtilsMessengerEXT)>,

    #[getset(get = "pub")]
    physical_device: vk::PhysicalDevice,

    #[getset(get = "pub")]
    physical_device_properties: vk::PhysicalDeviceProperties,

    #[getset(get = "pub")]
    queue_family_properties: vk::QueueFamilyProperties,

    #[getset(get = "pub")]
    device: ash::Device,

    #[getset(get = "pub")]
    instance: ash::Instance,

    #[getset(get = "pub")]
    graphics_queue_family_index: u32,

    #[getset(get = "pub")]
    compute_queue_family_index: u32,

    #[getset(get = "pub")]
    surface: vk::SurfaceKHR,

    #[getset(get = "pub")]
    command_pool: vk::CommandPool,
}

impl Drop for NjmDevice {
    fn drop(&mut self) {
        if !self.command_pool.is_null() {
            unsafe { self.device.destroy_command_pool(self.command_pool, None) };
        }
        if !self.surface.is_null() {
            let khr_surface_ins = self.get_khr_surface_instance();
            unsafe { khr_surface_ins.destroy_surface(self.surface, None) };
        }
        unsafe { self.device.destroy_device(None) };
        unsafe { self.instance.destroy_instance(None) };
        if let Some((ins, msgr)) = &self.debug_tools  {
            unsafe { ins.destroy_debug_utils_messenger(*msgr, None) };
        }
    }
}

impl NjmDevice {
    pub fn new(wnd: &Window) -> Self {
        let entry: Entry = unsafe { Entry::load().expect("failed to load ash::entry::Entry") };
        let app_name = CString::new(VULKAN_APPLICATION_NAME).unwrap();
        let eng_name = CString::new(VULKAN_ENGINE_NAME).unwrap();
        let app_info: vk::ApplicationInfo = vk::ApplicationInfo::default()
            .api_version(vk::API_VERSION_1_3)
            .application_version(make_api_version(0, 1, 0, 0))
            .application_name(app_name.as_c_str())
            .engine_version(make_api_version(0, 0, 0, 0))
            .engine_name(eng_name.as_c_str());
        //
        let mut desired_instance_extensions: Vec<*const i8> = vec![];
        let portability_ex: *const i8 = vk::KHR_PORTABILITY_ENUMERATION_NAME.as_ptr();
        let debug_utils_ex: *const i8 = debug_utils::NAME.as_ptr();
        let khr_surface_ex: *const i8 = vk::KHR_SURFACE_NAME.as_ptr();
        let khr_win32_surface_ex: *const i8 = vk::KHR_WIN32_SURFACE_NAME.as_ptr();
        #[cfg(any(target_os = "windows"))]
        {
            desired_instance_extensions.push(khr_surface_ex);
            desired_instance_extensions.push(khr_win32_surface_ex);
        }
        #[cfg(any(target_os = "macos", target_os = "ios"))]
        {
            desired_instance_extensions.push(portability_ex);
        }
        if ENABLE_VALIDATION_LAYERS {
            desired_instance_extensions.push(debug_utils_ex);
        }
        check_instance_extension_support(&entry, &desired_instance_extensions);

        let ins_create_flags: vk::InstanceCreateFlags =
            if cfg!(any(target_os = "macos", target_os = "ios")) {
                vk::InstanceCreateFlags::ENUMERATE_PORTABILITY_KHR
            } else {
                vk::InstanceCreateFlags::default()
            };
        let (layer_names, layer_names_ptrs) = get_layer_names_and_pointers();

        let ins_info: vk::InstanceCreateInfo = vk::InstanceCreateInfo::default()
            .application_info(&app_info)
            .enabled_extension_names(&desired_instance_extensions)
            .flags(ins_create_flags);
        if ENABLE_VALIDATION_LAYERS {
            check_validation_layer_support(&entry);
            ins_info.enabled_layer_names(&layer_names_ptrs);
        }

        // Instance
        let ins: ash::Instance = unsafe { entry.create_instance(&ins_info, None).unwrap() };
        let mut dbg_tools: Option<(debug_utils::Instance, vk::DebugUtilsMessengerEXT)> = None;
        if ENABLE_VALIDATION_LAYERS {
            dbg_tools = setup_debug_messenger(&entry, &ins);
        }

        // window surface
        let surf: Option<SurfaceKHR>;
        #[cfg(target_os = "windows")]
        {
            surf = create_vulkan_surface_win32(&entry, &ins, wnd);
        }
        if surf == None {
            panic!("NjmDevice::new : failed to create surface!");
        }

        // phy-dev, que-fam
        let mut m_phy_dev: Option<vk::PhysicalDevice> = None;
        let mut m_que_fam_idx: Option<u32> = None;
        let mut m_que_fam_prop: Option<vk::QueueFamilyProperties> = None;
        let mut m_que_cnt: Option<u32> = None;
        let mut m_phy_dev_prop: Option<vk::PhysicalDeviceProperties> = None;
        let flags_desired = [QueueFlags::GRAPHICS, QueueFlags::COMPUTE];
        // let flags_desired = [QueueFlags::GRAPHICS];
        let mut dev_found: bool = false;
        let devs = unsafe { ins.enumerate_physical_devices().unwrap() };
        for dev in devs {
            let dev_prop = unsafe { ins.get_physical_device_properties(dev) };
            let que_props = unsafe { ins.get_physical_device_queue_family_properties(dev) };
            let mut idx: u32 = 0;
            for prop in que_props {
                if flags_desired
                    .iter()
                    .all(|flag| prop.queue_flags.contains(*flag))
                {
                    dev_found = true;
                    m_phy_dev = Some(dev);
                    m_que_fam_idx = Some(idx);
                    m_que_fam_prop = Some(prop);
                    m_que_cnt = Some(prop.queue_count);
                    m_phy_dev_prop = Some(dev_prop);
                } else {
                }
                idx += 1;
            }
        }
        if !dev_found {
            panic!("no suitable gpu device found!");
        }

        // logical device
        let que_priority: [f32; 1] = [1.0];
        let dev_que_info: vk::DeviceQueueCreateInfo = vk::DeviceQueueCreateInfo::default()
            // .flags()
            .queue_family_index(m_que_fam_idx.unwrap())
            .queue_priorities(&que_priority);
        let que_infos = [dev_que_info];

        let swap_chain_ex_name: *const i8 = vk::KHR_SWAPCHAIN_NAME.as_ptr();
        let desired_device_extensions: Vec<*const i8> = vec![swap_chain_ex_name];

        let dev_info: vk::DeviceCreateInfo = vk::DeviceCreateInfo::default()
            .enabled_extension_names(&desired_device_extensions)
            .queue_create_infos(&que_infos);
        let m_device = unsafe {
            ins.create_device(m_phy_dev.unwrap(), &dev_info, None)
                .unwrap()
        };

        // command pool
        let cmd_pool_info = vk::CommandPoolCreateInfo::default()
            .queue_family_index(m_que_fam_idx.unwrap())
            .flags(vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER);
        let command_pool = unsafe { m_device.create_command_pool(&cmd_pool_info, None).unwrap() };

        // NjmDevice
        return Self {
            entry: entry,
            debug_tools: dbg_tools,
            instance: ins,
            surface: surf.unwrap(),
            device: m_device,
            physical_device: m_phy_dev.unwrap(),
            physical_device_properties: m_phy_dev_prop.unwrap(),
            queue_family_properties: m_que_fam_prop.unwrap(),
            graphics_queue_family_index: m_que_fam_idx.unwrap(),
            compute_queue_family_index: m_que_fam_idx.unwrap(),
            command_pool: command_pool,
        };
    }

    pub fn recreate_surface(&mut self, wnd: &Window) {
        let surf: Option<SurfaceKHR>;
        #[cfg(target_os = "windows")]
        {
            surf = create_vulkan_surface_win32(&self.entry, &self.instance, wnd);
        }
        if surf == None {
            panic!("NjmDevice::recreate_surface : failed to create surface!");
        }
        if !self.surface.is_null() {
            let khr_surface_ins = self.get_khr_surface_instance();
            unsafe { khr_surface_ins.destroy_surface(self.surface, None) };
        }
        self.surface = surf.unwrap();
    }

    pub fn find_memory_type(
        &self,
        requirements_memory_type_bits: u32,
        required_properties: vk::MemoryPropertyFlags,
    ) -> u32 {
        let m_properties = unsafe {
            self.instance
                .get_physical_device_memory_properties(self.physical_device)
        };
        for i in 0..m_properties.memory_type_count {
            if (requirements_memory_type_bits & (1 << i)) > 0
                && (m_properties.memory_types[i as usize].property_flags & required_properties)
                    == required_properties
            {
                return i;
            }
            // ((requirementsMemoryTypeBits & (1 << i)) &&
            // (memProperties.memoryTypes[i].propertyFlags & properties) == properties)
        }
        panic!("Failed to find suitable memory type!");
        return 0;
    }

    pub fn find_queue_family(
        &self,
        physical_device: vk::PhysicalDevice,
        surface: Option<vk::SurfaceKHR>,
        flags_desired: Vec<QueueFlags>,
    ) -> u32 {
        // assume queue family also need present support, if surface provided
        let que_props = unsafe {
            self.instance
                .get_physical_device_queue_family_properties(physical_device)
        };
        let mut idx: u32 = 0;
        for prop in que_props {
            if flags_desired
                .iter()
                .all(|flag| prop.queue_flags.contains(*flag))
            {
                // vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, i, surface, &presentSupport);
                if let Some(_surf) = surface {}
                return idx;
            }
            idx += 1;
        }
        panic!("Failed to find suitable queue family!");
        return 0;
    }

    pub fn create_buffer(
        &self,
        size: vk::DeviceSize,
        usage: vk::BufferUsageFlags,
        properties: vk::MemoryPropertyFlags,
    ) -> VkResult<(vk::Buffer, vk::DeviceMemory)> {
        let buffer_info = vk::BufferCreateInfo::default()
            .size(size)
            .usage(usage)
            .sharing_mode(SharingMode::EXCLUSIVE);
        let buf = unsafe { self.device.create_buffer(&buffer_info, None).unwrap() };
        let mem_req = unsafe { self.device.get_buffer_memory_requirements(buf) };
        let mem_ty_idx = self.find_memory_type(mem_req.memory_type_bits, properties);
        let mem_alloc_info = vk::MemoryAllocateInfo::default()
            .allocation_size(mem_req.size)
            .memory_type_index(mem_ty_idx);
        let mem = unsafe { self.device.allocate_memory(&mem_alloc_info, None).unwrap() };
        let _ = unsafe { self.device.bind_buffer_memory(buf, mem, 0).unwrap() };
        return Ok((buf, mem));
    }

    pub fn create_image(
        &self,
        img_info: vk::ImageCreateInfo,
        properties: vk::MemoryPropertyFlags,
    ) -> VkResult<(vk::Image, vk::DeviceMemory)> {
        let img = unsafe { self.device.create_image(&img_info, None).unwrap() };
        let mem_req = unsafe { self.device.get_image_memory_requirements(img) };
        let mem_ty_idx = self.find_memory_type(mem_req.memory_type_bits, properties);
        let mem_alloc_info = vk::MemoryAllocateInfo::default()
            .allocation_size(mem_req.size)
            .memory_type_index(mem_ty_idx);
        let mem = unsafe { self.device.allocate_memory(&mem_alloc_info, None).unwrap() };
        let _ = unsafe { self.device.bind_image_memory(img, mem, 0).unwrap() };
        return Ok((img, mem));
    }

    pub fn create_and_begin_once_cmdbuf(&self) -> vk::CommandBuffer {
        let cmdbuf_alloc_info = vk::CommandBufferAllocateInfo::default()
            .command_buffer_count(1)
            .command_pool(self.command_pool)
            .level(CommandBufferLevel::PRIMARY);
        let cmdbuf = unsafe {
            self.device
                .allocate_command_buffers(&cmdbuf_alloc_info)
                .unwrap()[0]
        };

        let begin_info =
            vk::CommandBufferBeginInfo::default().flags(CommandBufferUsageFlags::ONE_TIME_SUBMIT);
        let _ = unsafe {
            self.device
                .begin_command_buffer(cmdbuf, &begin_info)
                .unwrap()
        };
        return cmdbuf;
    }

    pub fn end_and_submit_once_cmdbuf(&self, cmdbuf: vk::CommandBuffer) {
        let _ = unsafe { self.device.end_command_buffer(cmdbuf).unwrap() };
        let bufs = [cmdbuf];

        let sub_info = vk::SubmitInfo::default().command_buffers(&bufs);
        let que = unsafe {
            self.device
                .get_device_queue(self.graphics_queue_family_index, 0)
        };
        let _ = unsafe {
            self.device
                .queue_submit(que, &[sub_info], vk::Fence::null())
                .unwrap()
        };
        let _ = unsafe { self.device.queue_wait_idle(que).unwrap() };

        unsafe { self.device.free_command_buffers(self.command_pool, &bufs) };
    }

    pub fn copy_buffer(&self, src: vk::Buffer, dst: vk::Buffer, size: vk::DeviceSize) {
        assert!(!src.is_null() && !dst.is_null());
        let cmdbuf = self.create_and_begin_once_cmdbuf();

        let copy_region = vk::BufferCopy::default()
            .size(size)
            .src_offset(0)
            .dst_offset(0);
        unsafe {
            self.device
                .cmd_copy_buffer(cmdbuf, src, dst, &[copy_region])
        };

        let mem_barrier = vk::MemoryBarrier::default()
            .src_access_mask(AccessFlags::TRANSFER_WRITE)
            .dst_access_mask(AccessFlags::SHADER_READ);
        unsafe {
            self.device.cmd_pipeline_barrier(
                cmdbuf,
                PipelineStageFlags::TRANSFER,
                PipelineStageFlags::VERTEX_INPUT,
                DependencyFlags::empty(),
                &[mem_barrier],
                &[],
                &[],
            )
        };

        self.end_and_submit_once_cmdbuf(cmdbuf);
    }

    pub fn copy_buffer_to_image(&self, buf: vk::Buffer, img: vk::Image, width: u32, height: u32) {
        let cmd_buf = self.create_and_begin_once_cmdbuf();

        let sub_res_range = vk::ImageSubresourceRange::default()
            .aspect_mask(ImageAspectFlags::COLOR)
            .base_array_layer(0)
            .base_mip_level(0)
            .layer_count(1)
            .level_count(1);
        let img_mem_barrier = vk::ImageMemoryBarrier::default()
            .subresource_range(sub_res_range)
            .old_layout(ImageLayout::UNDEFINED)
            .new_layout(ImageLayout::TRANSFER_DST_OPTIMAL)
            .src_access_mask(AccessFlags::NONE)
            .dst_access_mask(AccessFlags::TRANSFER_WRITE)
            .src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
            .dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
            .image(img);
        unsafe {
            self.device.cmd_pipeline_barrier(
                cmd_buf,
                PipelineStageFlags::TOP_OF_PIPE,
                PipelineStageFlags::TRANSFER,
                DependencyFlags::empty(),
                &[],
                &[],
                &[img_mem_barrier],
            )
        };

        let img_sub_res_layer = vk::ImageSubresourceLayers::default()
            .aspect_mask(ImageAspectFlags::COLOR)
            .mip_level(0)
            .base_array_layer(0)
            .layer_count(1);
        let copy_info = vk::BufferImageCopy::default()
            .buffer_offset(0)
            .buffer_image_height(0)
            .buffer_row_length(0)
            .image_extent(Extent3D::default().width(width).height(height).depth(1))
            .image_offset(Offset3D::default().x(0).y(0).z(0))
            .image_subresource(img_sub_res_layer);
        unsafe {
            self.device.cmd_copy_buffer_to_image(
                cmd_buf,
                buf,
                img,
                ImageLayout::TRANSFER_DST_OPTIMAL,
                &[copy_info],
            )
        };

        let sub_res_range_after_copy = vk::ImageSubresourceRange::default()
            .aspect_mask(ImageAspectFlags::COLOR)
            .base_array_layer(0)
            .base_mip_level(0)
            .layer_count(1)
            .level_count(1);
        let img_mem_barrier_after_copy = vk::ImageMemoryBarrier::default()
            .subresource_range(sub_res_range_after_copy)
            .old_layout(ImageLayout::TRANSFER_DST_OPTIMAL)
            .new_layout(ImageLayout::SHADER_READ_ONLY_OPTIMAL)
            .src_access_mask(AccessFlags::TRANSFER_WRITE)
            .dst_access_mask(AccessFlags::SHADER_READ)
            .src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
            .dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
            .image(img);
        unsafe {
            self.device.cmd_pipeline_barrier(
                cmd_buf,
                PipelineStageFlags::TRANSFER,
                PipelineStageFlags::FRAGMENT_SHADER,
                DependencyFlags::empty(),
                &[],
                &[],
                &[img_mem_barrier_after_copy],
            )
        };

        self.end_and_submit_once_cmdbuf(cmd_buf);
    }

    pub fn get_graphics_queue(&self) -> vk::Queue {
        let que = unsafe {
            self.device.get_device_queue(self.graphics_queue_family_index, 0)
        };
        return que;
    }

    // ext device
    pub fn get_khr_swapchain_device(&self) -> swapchain::Device {
        return swapchain::Device::new(&self.instance, &self.device);
    }

    pub fn get_khr_surface_instance(&self) -> surface::Instance {
        return  surface::Instance::new(&self.entry, &self.instance);
    }

}
